#include "Interpreter.h"
#include "Symbol.h"
#include "Lex.h"
#include "CompilierError.h"

#define GET_STACK_TOP() m_Stack.top(); m_Stack.pop();

CInterpreter::CInterpreter() : m_PC(0), m_Paused(false)
{
#define BB(xx) m_Handler[OP_##xx] = &CInterpreter::Handle_OP_##xx;
#include "Opcode.h"

#define CC(xx) m_InternalFunctions[#xx] = &CInterpreter::##xx;
#include "Functions.h"
}

size_t CInterpreter::Generate(ByteCode& code)
{
  m_ByteCode.push_back(code);
  return m_ByteCode.size() - 1;
}

void CInterpreter::FixJMP(size_t pc, size_t dst)
{
  if (pc >= m_ByteCode.size() || dst >= m_ByteCode.size()) {
    return;
  }
  m_ByteCode[pc].v.n = dst;
}

void CInterpreter::Run()
{
  while (1) {
    if (m_Paused) {
      continue;
    }

    if (m_PC >= m_ByteCode.size()) {
      break;
    }

    ByteCode code = m_ByteCode[m_PC];
    Execute(code);
    m_PC++;
  }
}

void CInterpreter::Execute(ByteCode& code)
{
  if (code.opcode < 0 || code.opcode >= OP_MAX) {
    return;
  }
  (this->*(m_Handler[code.opcode]))(code);
}

void CInterpreter::Reset()
{
  m_PC = 0;
  m_Paused = false;
  while (!m_Stack.empty()) {
    m_Stack.pop();
  }
}

void CInterpreter::Pause()
{
  m_Paused = true;
}

void CInterpreter::Resume()
{
  m_Paused = false;
}

//////////////////////////////////////////////////////////////////////////
void CInterpreter::Handle_OP_LD(ByteCode& code)
{
  Value v = g_SymbolMgr.GetValue((int)code.v.n);
  if (v.type == VT_UNKNOWN) {
    ReportError(-1, "Unknown variable\n");
  }
  m_Stack.push(v);
}

void CInterpreter::Handle_OP_LDC(ByteCode& code)
{
  m_Stack.push(code.v);
}

void CInterpreter::Handle_OP_SV(ByteCode& code)
{
  Value v = GET_STACK_TOP();
  if (!g_SymbolMgr.SetValue((int)code.v.n, v)) {
    ReportError(-1, "Unknown variable\n");
  }
}

void CInterpreter::Handle_OP_POP(ByteCode& code)
{
  m_Stack.pop();
}

void CInterpreter::Handle_OP_ADD(ByteCode& code)
{
  if (m_Stack.size() < 2) {
    ReportError(-1, "Invalid expression\n");
  }

  Value v1 = GET_STACK_TOP();
  Value v2 = GET_STACK_TOP();

  if (v1.type == VT_UNKNOWN || v2.type == VT_UNKNOWN || v1.type != v2.type) {
    ReportError(-1, "Invalid variable value\n");
  }
  Value v = { v1.type };
  if (v1.type == VT_STRING && v2.type == VT_STRING) {
    v.s = v2.s + v1.s;
  } else {
    v.n = v1.n + v2.n;
  }
  m_Stack.push(v);
}

#define OP_ARITH(op) \
  if (m_Stack.size() < 2) { \
    ReportError(-1, "Invalid expression\n"); \
  } \
   \
  Value v1 = GET_STACK_TOP(); \
  Value v2 = GET_STACK_TOP(); \
   \
  if (v1.type != VT_NUMBER || v1.type != v2.type) { \
    ReportError(-1, "Invalid variable value\n"); \
  } \
  Value v = { v1.type }; \
  v.n = v2.n op v1.n; \
  m_Stack.push(v);

void CInterpreter::Handle_OP_SUB(ByteCode& code)
{
  OP_ARITH(-);
}

void CInterpreter::Handle_OP_MUL(ByteCode& code)
{
  OP_ARITH(*);
}

void CInterpreter::Handle_OP_DIV(ByteCode& code)
{
  OP_ARITH(/);
}

void CInterpreter::Handle_OP_MOD(ByteCode& code)
{
  OP_ARITH(%);
}

void CInterpreter::Handle_OP_CALL(ByteCode& code)
{
  Value v = GET_STACK_TOP();
  if (v.type != VT_NUMBER) {
    ReportError(-1, "Invalid call\n");
  }

  std::deque<Value> tmp;
  for (int i = 0; i < (int)v.n; i++) {
    Value v_ = GET_STACK_TOP();
    tmp.push_front(v_);
  }

  CallFunction(code.v.s, tmp);
}

void CInterpreter::CallFunction(std::string& name, std::deque<Value>& params)
{
  auto it = m_InternalFunctions.find(name);
  if (it == m_InternalFunctions.end()) {
    // call others
    size_t f = g_SymbolMgr.GetFunction(name);
    if (f == -1) {
      ReportError(-1, "Cannot find function: %s", name.c_str());
    }
    Value ret_addr = { VT_NUMBER, m_PC };
    m_Stack.push(ret_addr);
    for (int i = (int)params.size()-1; i >= 0; i--) {
      m_Stack.push(params[i]);
    }
    m_PC = f;
    return;
  }
  (this->*(it->second))(params);
}

void CInterpreter::Handle_OP_CMP(ByteCode& code)
{
  Value v2 = GET_STACK_TOP();
  Value v1 = GET_STACK_TOP();
  if (v1.type != v2.type) {
    ReportError(-1, "Mismatching type.");
  }
  bool is_number = (v1.type == VT_NUMBER);
  int res = 0;
  switch (code.v.n)
  {
  case __EQUAL:
    res = (is_number ? v1.n == v2.n : v1.s == v2.s) ? 1 : 0;
    break;
  case __NOTEQUAL:
    res = (is_number ? v1.n != v2.n : v1.s != v2.s) ? 1 : 0;
    break;
  case __LESS:
    res = (is_number ? v1.n < v2.n : v1.s < v2.s) ? 1 : 0;
    break;
  case __GREAT:
    res = (is_number ? v1.n > v2.n : v1.s > v2.s) ? 1 : 0;
    break;
  case __LEQUAL:
    res = (is_number ? v1.n <= v2.n : v1.s <= v2.s) ? 1 : 0;
    break;
  case __GEQUAL:
    res = (is_number ? v1.n >= v2.n : v1.s >= v2.s) ? 1 : 0;
    break;
  }
  Value v = { VT_NUMBER };
  v.n = res;
  m_Stack.push(v);
}

void CInterpreter::Handle_OP_JMP(ByteCode& code)
{
  m_PC = (size_t)code.v.n;
}

void CInterpreter::Handle_OP_JCC(ByteCode& code)
{
  Value v = GET_STACK_TOP();
  bool res = false;
  if (v.type == VT_NUMBER && v.n) {
    res = true;
  }
  if (v.type == VT_STRING && v.s != "") {
    res = true;
  }
  if (!res) {
    m_PC = (size_t)code.v.n;
  }
}

void CInterpreter::Handle_OP_NOP(ByteCode& code)
{
}

void CInterpreter::Handle_OP_RET(ByteCode& code)
{
  Value v = GET_STACK_TOP();
  m_PC = (size_t)v.n;
}

void CInterpreter::Handle_OP_MAX(ByteCode& code)
{
#if 0
  fuck off
#endif
}
